/*----------------------------------------------------------------------------
 * Copyright (c) <2013-2015>, <Huawei Technologies Co., Ltd>
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior written
 * permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *---------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 *---------------------------------------------------------------------------*/

#include "host_common.h"
#include "hifmc_common.h"
#include "hifmc100.h"
/*****************************************************************************/
/* MXIC QE(bit) include in Status Register */
#define MX_SPI_NOR_SR_QE_SHIFT    6
#define MX_SPI_NOR_SR_QE_MASK    (1 << MX_SPI_NOR_SR_QE_SHIFT)
#define MX_SPI_NOR_GET_QE_BY_SR(sr)    (((sr) & MX_SPI_NOR_SR_QE_MASK) \
                        >> MX_SPI_NOR_SR_QE_SHIFT)

extern u_char hifmc100_read_reg(struct spi *spi, u_char cmd);
/*
   enable QE bit if 4X R/W is supported by MXIC "25L(256/257)35(E/F)" SPI
*/
int spi_mx25l25635e_qe_enable(struct spi *spi)
{
    unsigned char status, op;
    unsigned int reg;
    const char *str[] = {"Disable", "Enable"};
    struct spinor_host *host = (struct spinor_host *)spi->host;

    op = spi_is_quad(spi);

    MTD_PR(QE_DBG, "\t|*-Start MXIC SPI Nor %s Quad.\n", str[op]);
    status = hifmc100_read_reg(spi, SPI_CMD_RDSR);
    MTD_PR(QE_DBG, "\t||-Read Status Register[%#x]%#x\n", SPI_CMD_RDSR,
           status);
    if (MX_SPI_NOR_GET_QE_BY_SR(status) == op) {
        MTD_PR(QE_DBG, "\t|*-Quad was %sd, status:%#x\n", str[op],
               status);
        return op;
    }

    spi->driver->write_enable(spi);

    if (op) {
        status |= MX_SPI_NOR_SR_QE_MASK;
    } else {
        status &= ~MX_SPI_NOR_SR_QE_MASK;
    }
    writeb(status, host->membase);

    reg = FMC_CMD_CMD1(SPI_CMD_WRSR);
    reg_write(host, reg, FMC_CMD);

    reg = OP_CFG_FM_CS(spi->cs);
    reg_write(host, reg, FMC_OP_CFG);

    reg = FMC_DATA_NUM_CNT(SPI_NOR_SR_LEN);
    reg_write(host, reg, FMC_DATA_NUM);

    reg = FMC_OP_CMD1_EN(ENABLE)
          | FMC_OP_WRITE_DATA_EN(ENABLE)
          | FMC_OP_REG_OP_START;
    reg_write(host, reg, FMC_OP);

    FMC_CMD_WAIT_CPU_FINISH(host);

    spi->driver->wait_ready(spi);

    status = hifmc100_read_reg(spi, SPI_CMD_RDSR);
    if (MX_SPI_NOR_GET_QE_BY_SR(status) == op)
        MTD_PR(QE_DBG, "\t||-%s Quad success, status:%#x.\n", str[op],
               status);
    else {
        ERR_MSG(" %s Quad failed! reg: %#x\n", str[op], status);
    }

    MTD_PR(QE_DBG, "\t|*-End MXIC SPI Nor %s Quad.\n", str[op]);

    return op;
}

/*----------------------------------------------------------------------------*/
#ifdef LOSCFG_DRIVERS_DTR_MODE_SUPPORT
/*****************************************************************************/
int spi_mxic_output_driver_strength_set(struct spi *spi, int dtr_en)
{
    unsigned char status, config;
    unsigned short reg;
    unsigned short val = 0;
    unsigned int ix, regval;
    struct spinor_host *host = (struct spinor_host *)spi->host;
    struct spi_op *read = spi->read;
    /************************************************************************/
    /* DC[1:0]  |  Numbers of Dummy clock cycles|  Quad IO DTR Read     */
    /*   00(default)|       6       |   54      */
    /*   01     |       6       |   54      */
    /*   10     |       8       |   70/80R      */
    /*   11     |       10      |   84/100R     */
    /************************************************************************/
    unsigned int str_dummy[] = {
        DTR_DUMMY_CYCLES_6,      DTR_RDCR_DC_MASK(0),
        DTR_DUMMY_CYCLES_6,      DTR_RDCR_DC_MASK(1),
        DTR_DUMMY_CYCLES_8,      DTR_RDCR_DC_MASK(2),
        DTR_DUMMY_CYCLES_10,     DTR_RDCR_DC_MASK(3),
        0,      0,
    };

    /* get the RDCR and RDSR */
    spi->driver->wait_ready(spi);

    /* setting the DC value to match high system clock */
    config = hifmc100_read_reg(spi, SPI_CMD_RDCR_MX);
    MTD_PR(DTR_DBG, "Get Config Register[%#x]\n", config);

    /* check the QE value */
    status = hifmc100_read_reg(spi, SPI_CMD_RDSR);
    MTD_PR(DTR_DBG, "Get Status Register[%#x]\n", status);

    reg = ((unsigned short)config << SPI_NOR_CR_SHIFT) | status;

    if (dtr_en == ENABLE) {
        /* setting DC value */
        MTD_PR(DTR_DBG, "Get the dummy value[%#x]\n", read->dummy);
        for (ix = 0; str_dummy[ix]; ix += 2) {
            if (read->dummy < str_dummy[ix]) {
                break;
            }
            val = (unsigned short)str_dummy[ix + 1];
        }
    } else {
        val = DTR_RDCR_DC_MASK(0);
    }

    reg = DTR_RDCR_DC_BIT_CLR(reg) | (val << DTR_RDSR_DC_SHIFT);

    spi->driver->write_enable(spi);
    writew(reg, host->membase);
    MTD_PR(DTR_DBG, "Write IO[%p]%#x\n", host->membase,
           *(unsigned short *)host->membase);
    regval = FMC_CMD_CMD1(SPI_CMD_WRSR);
    reg_write(host, regval, FMC_CMD);
    MTD_PR(DTR_DBG, " Set CMD[%#x]%#x\n", FMC_CMD, regval);

    regval = OP_CFG_FM_CS(spi->cs) | OP_CFG_OEN_EN;

    reg_write(host, regval, FMC_OP_CFG);
    MTD_PR(DTR_DBG, " Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);

    regval = FMC_DATA_NUM_CNT(SPI_NOR_SR_LEN + SPI_NOR_CR_LEN);
    reg_write(host, regval, FMC_DATA_NUM);
    MTD_PR(DTR_DBG, " Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);

    regval = FMC_OP_CMD1_EN(ENABLE)
             | FMC_OP_WRITE_DATA_EN(ENABLE)
             | FMC_OP_REG_OP_START;
    reg_write(host, regval, FMC_OP);
    MTD_PR(DTR_DBG, " Set OP[%#x]%#x\n", FMC_OP, regval);

    FMC_CMD_WAIT_CPU_FINISH(host);

    config = hifmc100_read_reg(spi, SPI_CMD_RDCR_MX);
    if ((config >> DTR_RDCR_DC_SHIFT) != (unsigned char)val) {
        printf("* Set DC dummy fail.\n");
        return -1;
    }
    return 0;
}
/*----------------------------------------------------------------------*/
unsigned int spi_mxic_check_spi_dtr_support(struct spi *spi)
{
    unsigned int regval, rd_sfdp_dummy = 1, sfdp_addrcycle = 3;
    struct spinor_host *host = (struct spinor_host *)spi->host;

    /* get the RDCR and RDSR */
    spi->driver->wait_ready(spi);

    /* Read the Serial Flash Discoverable Parameter (SFDP) */
    reg_write(host, SPI_CMD_RD_SFDP, FMC_CMD);
    MTD_PR(DTR_DBG, "\t   Set CMD[%#x]%#x\n", FMC_CMD, SPI_CMD_RD_SFDP);

    regval = OP_CFG_FM_CS(spi->cs) | OP_CFG_OEN_EN
             | OP_CFG_ADDR_NUM(sfdp_addrcycle)
             | OP_CFG_DUMMY_NUM(rd_sfdp_dummy);
    reg_write(host, regval, FMC_OP_CFG);
    MTD_PR(DTR_DBG, "\t\t   Set OP_CFG[%#x]%#x\n", FMC_OP_CFG, regval);

    regval = FMC_DATA_NUM_CNT(SFDP_BUF_LEN);
    reg_write(host, regval, FMC_DATA_NUM);
    MTD_PR(DTR_DBG, "\t   Set DATA_NUM[%#x]%#x\n", FMC_DATA_NUM, regval);

    regval = FMC_OP_DUMMY_EN(ENABLE)
             | FMC_OP_CMD1_EN(ENABLE)
             | FMC_OP_ADDR_EN(ENABLE)
             | FMC_OP_READ_DATA_EN(ENABLE)
             | FMC_OP_REG_OP_START;
    reg_write(host, regval, FMC_OP);
    MTD_PR(DTR_DBG, "\t   Set OP[%#x]%#x\n", FMC_OP, regval);

    FMC_CMD_WAIT_CPU_FINISH(host);

    regval = readb((char *)host->membase + SFDP_DTR_BYTE_SHIFT);
    MTD_PR(DTR_DBG, "\t the dtr_mode_support is: %#x\n", regval);

    /* get the DTR mode support bit */
    spi->dtr_mode_support = (regval >> SFDP_DTR_BIT_SHIFT)
                            & SFDP_DTR_BIT_MASK;

    //spi->dtr_mode_support = 0;
    return spi->dtr_mode_support;
}
#endif /* LOSCFG_DRIVERS_DTR_MODE_SUPPORT */

